package sqlx

import (
	"database/sql"
	"fmt"
	"gitee.com/gopher2011/sqlx/reflectx"
	"reflect"
)

// Stmt 是 sql.Stmt 的扩展。
type Stmt struct {
	*sql.Stmt
	unsafe bool
	Mapper *reflectx.Mapper
}

// Unsafe 返回一个Stmt版本，当SQL结果中的列在目标结构中没有字段时，它将以静默方式成功扫描。
func (s *Stmt) Unsafe() *Stmt {
	return &Stmt{Stmt: s.Stmt, unsafe: true, Mapper: s.Mapper}
}

// qStmt 是一个未公开的包装器，通过实现这些接口并忽略`query`参数，您可以将Stmt用作 IQuery ＆Execer。
type qStmt struct{ *Stmt }

// Query database/sql包下的原生的 Query()
func (q *qStmt) Query(query string, args ...interface{}) (*sql.Rows, error) {
	return q.Stmt.Query(args...)
}

// QueryX *sqlx包下的 QueryX()
func (q *qStmt) QueryX(query string, args ...interface{}) (*Rows, error) {
	r, err := q.Stmt.Query(args...)
	if err != nil {
		return nil, err
	}
	return &Rows{Rows: r, unsafe: q.Stmt.unsafe, Mapper: q.Stmt.Mapper}, err
}

// QueryRowX *sqlx包下的 QueryRowX()
func (q *qStmt) QueryRowX(query string, args ...interface{}) *Row {
	rows, err := q.Stmt.Query(args...)
	return &Row{rows: rows, err: err, unsafe: q.Stmt.unsafe, Mapper: q.Stmt.Mapper}
}

// Exec database/sql 包下的原生的 stmt.Exec()
func (q *qStmt) Exec(query string, args ...interface{}) (sql.Result, error) {
	return q.Stmt.Exec(args...)
}

// ExecPanic 原(MustExec) (panic) using this statement.
//  请注意，错误输出的查询部分将为空白，因为Stmt不会公开其查询。
//  任何占位符参数都将替换为提供的args。
func (s *Stmt) ExecPanic(args ...interface{}) sql.Result {
	return ExecPanic(&qStmt{s}, "", args...)
}

// QueryRowX using this statement.
//  任何占位符参数都将替换为提供的args。
func (s *Stmt) QueryRowX(args ...interface{}) *Row {
	qs := &qStmt{s}
	return qs.QueryRowX("", args...)
}

// QueryX using this statement.
//  任何占位符参数都将替换为提供的args。
func (s *Stmt) QueryX(args ...interface{}) (*Rows, error) {
	qs := &qStmt{s}
	return qs.QueryX("", args...)
}

// Select 查询多行记录，并将结果扫描进<pointer>中，参数<pointer>可以是 struct/*struct。
//  任何占位符参数都将替换为提供的args。
func (s *Stmt) Select(pointer interface{}, args ...interface{}) error {
	return Select(&qStmt{s}, pointer, "", args...)
}

// Take (原Get) 查询一条记录，并将结果扫描进<pointer>中，参数<pointer>可以是 struct/*struct。
//  任何占位符参数都将替换为提供的args。如果结果集为空，则返回错误。
func (s *Stmt) Take(pointer interface{}, args ...interface{}) error {
	return Take(&qStmt{s}, pointer, "", args...)
}

// StmtN (原NamedStmt) 是执行命名参数查询的预加载语句。
//  准备如何执行 NamedQuery，但在执行时传入结构体或map。
type StmtN struct {
	Stmt        *Stmt
	Params      []string
	QueryString string
}

// Unsafe creates an unsafe version of the NamedStmt
func (n *StmtN) Unsafe() *StmtN {
	r := &StmtN{Params: n.Params, Stmt: n.Stmt, QueryString: n.QueryString}
	r.Stmt.unsafe = true
	return r
}

// Close closes the named statement.
func (n *StmtN) Close() error {
	return n.Stmt.Close()
}

// Query executes a named statement using the struct argument, returning rows.
// Any named placeholder parameters are replaced with fields from arg.
func (n *StmtN) Query(arg interface{}) (*sql.Rows, error) {
	args, err := bindAnyArgs(n.Params, arg, n.Stmt.Mapper)
	if err != nil {
		return nil, err
	}
	return n.Stmt.Query(args...)
}

// QueryRow executes a named statement against the database.  Because sqlx cannot
// create a *sql.Row with an error condition pre-set for binding errors, sqlx
// returns a *sqlx.Row instead.
// Any named placeholder parameters are replaced with fields from arg.
func (n *StmtN) QueryRow(arg interface{}) *Row {
	args, err := bindAnyArgs(n.Params, arg, n.Stmt.Mapper)
	if err != nil {
		return &Row{err: err}
	}
	return n.Stmt.QueryRowX(args...)
}

// Exec executes a named statement using the struct passed.
// Any named placeholder parameters are replaced with fields from arg.
func (n *StmtN) Exec(arg interface{}) (sql.Result, error) {
	args, err := bindAnyArgs(n.Params, arg, n.Stmt.Mapper)
	if err != nil {
		return *new(sql.Result), err
	}
	return n.Stmt.Exec(args...)
}

// ExecPanic (原MustExec) execs a NamedStmt, panicing on error
// Any named placeholder parameters are replaced with fields from arg.
func (n *StmtN) ExecPanic(arg interface{}) sql.Result {
	res, err := n.Exec(arg)
	if err != nil {
		panic(err)
	}
	return res
}

// QueryX using this NamedStmt
// Any named placeholder parameters are replaced with fields from arg.
func (n *StmtN) QueryX(arg interface{}) (*Rows, error) {
	r, err := n.Query(arg)
	if err != nil {
		return nil, err
	}
	return &Rows{Rows: r, Mapper: n.Stmt.Mapper, unsafe: isUnsafe(n)}, err
}

// QueryRowX this NamedStmt.  Because of limitations with QueryRow, this is
// an alias for QueryRow.
// Any named placeholder parameters are replaced with fields from arg.
func (n *StmtN) QueryRowX(arg interface{}) *Row {
	return n.QueryRow(arg)
}

// Select 查询多行记录，并将结果扫描进<pointer>中，参数<pointer>可以是 struct/*struct。
//  任何占位符参数都将替换为提供的args。
func (n *StmtN) Select(pointer interface{}, arg interface{}) error {
	rows, err := n.QueryX(arg)
	if err != nil {
		return err
	}
	// if something happens here, we want to make sure the rows are Closed
	defer rows.Close()
	return scanAll(rows, pointer, false)
}

// Take (原Get) 查询一条记录，并将结果扫描进<pointer>中，参数<pointer>可以是 struct/*struct。
//  任何占位符参数都将替换为提供的args。如果结果集为空，则返回错误。
func (n *StmtN) Take(pointer interface{}, arg interface{}) error {
	r := n.QueryRowX(arg)
	return r.scanAny(pointer, false)
}

// Conn 是对 sql.Conn 的扩展。
type Conn struct {
	*sql.Conn
	driverName string
	unsafe     bool
	Mapper     *reflectx.Mapper
}

// DB 是 sql.DB 的包装，在打开时跟踪driverName，通常用于使用正确的 bindVars 自动绑定命名查询。
type DB struct {
	*sql.DB
	driverName string
	unsafe     bool
	Mapper     *reflectx.Mapper
}

// NewDB 输入一个 *sql.DB 和 数据库驱动，返回一个 *sqlx.DB实例。
//  这是 sqlx.DB 的构造函数。
//  原始数据库的driverName是命名查询支持所必需的。
func NewDB(db *sql.DB, driverName string) *DB {
	return &DB{DB: db, driverName: driverName, Mapper: mapper()}
}

// Open 与sql.Open相同，但返回 *sqlx.DB。
func Open(driverName, dataSourceName string) (*DB, error) {
	db, err := sql.Open(driverName, dataSourceName)
	if err != nil {
		return nil, err
	}
	return &DB{DB: db, driverName: driverName, Mapper: mapper()}, err
}

// Connect 连接到数据库并通过 ping 进行验证，ping失败则关闭数据库连接，并返回error。
func Connect(driverName, dataSourceName string) (*DB, error) {
	db, err := Open(driverName, dataSourceName)
	if err != nil {
		return nil, err
	}
	err = db.Ping()
	if err != nil {
		db.Close()
		return nil, err
	}
	return db, nil
}

// MapperFunc 使用默认的 sqlx.struct 标记和提供的 mapper 函数为此数据库设置一个新的 mapper。
func (db *DB) MapperFunc(mf func(string) string) {
	db.Mapper = reflectx.NewMapperFunc("d", mf)
}

// Unsafe 返回一个DB版本，当SQL结果中的列在目标结构中没有字段时，它将以静默方式成功扫描。
// 从此数据库创建的sqlx.Stmt和sqlx.Tx将继承其安全行为。
func (db *DB) Unsafe() *DB {
	return &DB{DB: db.DB, driverName: db.driverName, unsafe: true, Mapper: db.Mapper}
}

// BeginX 开启一个事务，(区别于原生Begin) 并返回 *sqlx.Tx 而不是 *sql.Tx。
func (db *DB) BeginX() (*Tx, error) {
	tx, err := db.DB.Begin()
	if err != nil {
		return nil, err
	}
	return &Tx{Tx: tx, driverName: db.driverName, unsafe: db.unsafe, Mapper: db.Mapper}, err
}

// MustBegin starts a transaction, and panics on error.  Returns an *sqlx.Tx instead
// of an *sql.Tx.
func (db *DB) BeginPanic() *Tx {
	tx, err := db.BeginX()
	if err != nil {
		panic(err)
	}
	return tx
}

// MustExec (panic) runs MustExec using this database.
// Any placeholder parameters are replaced with supplied args.
func (db *DB) ExecPanic(query string, args ...interface{}) sql.Result {
	return ExecPanic(db, query, args...)
}

// DriverName 返回传递给该数据库的 Open() 函数的driverName。
//  (获取当前数据库的驱动名称)
func (db *DB) DriverName() string {
	return db.driverName
}

// Rebind 将查询从 Question 转换为数据库驱动程序的 bindVar 类型。
func (db *DB) Rebind(query string) string {
	return Rebind(BindType(db.driverName), query)
}

// BindNamed 使用数据库驱动程序的 bindvar 类型绑定查询。
func (db *DB) BindNamed(query string, arg interface{}) (string, []interface{}, error) {
	return bindNamedMapper(BindType(db.driverName), query, arg, db.Mapper)
}

// QueryX 查询数据库并返回 *sqlx.Rows。
// 	任何占位符参数都将替换为提供的args。
func (db *DB) QueryX(query string, args ...interface{}) (*Rows, error) {
	r, err := db.DB.Query(query, args...)
	if err != nil {
		return nil, err
	}
	return &Rows{Rows: r, unsafe: db.unsafe, Mapper: db.Mapper}, err
}

// QueryRowX 查询数据库并返回 *sqlx.Row。
// 	任何占位符参数都将替换为提供的args。
func (db *DB) QueryRowX(query string, args ...interface{}) *Row {
	rows, err := db.DB.Query(query, args...)
	return &Row{rows: rows, err: err, unsafe: db.unsafe, Mapper: db.Mapper}
}

// PrepareX 返回 *sqlx.Stmt 而不是 *sql.Stmt
func (db *DB) PrepareX(query string) (*Stmt, error) {
	return PrepareX(db, query)
}

// PrepareNamed 返回一个 *sqlx.NamedStmt，*StmtN 支持命名参数。
func (db *DB) PrepareN(query string) (*StmtN, error) {
	return prepareNamed(db, query)
}

// NamedQuery using this DB.
// Any named placeholder parameters are replaced with fields from arg.
func (db *DB) QueryN(query string, arg interface{}) (*Rows, error) {
	return NamedQuery(db, query, arg) // 该函数在 6_util_named.go
}

// NamedExec using this DB.
// Any named placeholder parameters are replaced with fields from arg.
func (db *DB) ExecN(query string, arg interface{}) (sql.Result, error) {
	return NamedExec(db, query, arg) // 该函数在 6_util_named.go
}


// Select 查询多条数据，并将结果扫描进<pointer>中。参数<pointer>必须是切片(slice)类型。
//  <querySql>是查询SQL语句，<args>是查询SQL语句需要的参数。
// 	任何占位符参数都将替换为提供的args。
func (db *DB) Select(pointer interface{}, querySql string, args ...interface{}) error {
	return Select(db, pointer, querySql, args...)
}

// Take 查询任意一条数据，并将结果扫描进<pointer>中。参数<pointer>可以是 struct/*struct。
//  <querySql>是查询SQL语句，<args>是查询SQL语句需要的参数。
// 	任何占位符参数都将替换为提供的args。如果结果集为空，则返回错误。
func (db *DB) Take (pointer interface{}, querySql string, args ...interface{}) error {
	return Take(db, pointer, querySql, args...)

}

// Tx 是对 sql.Tx 的扩展。
type Tx struct {
	*sql.Tx
	driverName string
	unsafe     bool
	Mapper     *reflectx.Mapper
}

// DriverName 返回开始该事务的数据库使用的 driverName。
func (tx *Tx) DriverName() string {
	return tx.driverName
}

// Rebind 事务的bindVar类型的查询。
func (tx *Tx) Rebind(query string) string {
	return Rebind(BindType(tx.driverName), query)
}

// BindNamed binds a query within a transaction's bindvar type.
func (tx *Tx) BindNamed(query string, arg interface{}) (string, []interface{}, error) {
	return bindNamedMapper(BindType(tx.driverName), query, arg, tx.Mapper)
}

// Unsafe returns a version of Tx which will silently succeed to scan when
// columns in the SQL result have no fields in the destination struct.
func (tx *Tx) Unsafe() *Tx {
	return &Tx{Tx: tx.Tx, driverName: tx.driverName, unsafe: true, Mapper: tx.Mapper}
}

// QueryX 区别于 database/sql 包下的原生 Tx.Query()
// 	任何占位符参数都将替换为提供的args。
func (tx *Tx) QueryX(query string, args ...interface{}) (*Rows, error) {
	r, err := tx.Tx.Query(query, args...)
	if err != nil {
		return nil, err
	}
	return &Rows{Rows: r, unsafe: tx.unsafe, Mapper: tx.Mapper}, err
}

// QueryRowX 区别于 database/sql 包下的原生 Tx.QueryRow()
// 	任何占位符参数都将替换为提供的args。
func (tx *Tx) QueryRowX(query string, args ...interface{}) *Row {
	rows, err := tx.Tx.Query(query, args...)
	return &Row{rows: rows, err: err, unsafe: tx.unsafe, Mapper: tx.Mapper}
}

// QueryN (原NamedQuery) 区别于原生的 Tx.Query()，它支持命名参数查询多条记录。
// 	任何命名的占位符参数都将替换为arg中的字段。
func (tx *Tx) QueryN(query string, arg interface{}) (*Rows, error) {
	return NamedQuery(tx, query, arg)
}

// ExecN (原NamedExec) 区别于原生的 Tx.Query()，它支持命名参数执行SQL语句。
// 	任何命名的占位符参数都将替换为arg中的字段。
func (tx *Tx) ExecN(query string, arg interface{}) (sql.Result, error) {
	return NamedExec(tx, query, arg)
}

// ExecPanic (原MustExec)区别于原生的 Tx.Exec()，如果执行出错，它内部会 panic。
// 	任何占位符参数都将替换为提供的args。
func (tx *Tx) ExecPanic(query string, args ...interface{}) sql.Result {
	return ExecPanic(tx, query, args...)
}

// Select 通过事务查询多条数据，并将结果扫描进<pointer>中，参数<pointer>可以是 struct/*struct。
// 	任何占位符参数都将替换为提供的args。
func (tx *Tx) Select(pointer interface{}, query string, args ...interface{}) error {
	return Select(tx, pointer, query, args...)
}

// Take 通过事务查询任意一条数据，并将结果扫描进<pointer>中，参数<pointer>可以是 struct/*struct。
// 	任何占位符参数都将替换为提供的参数。如果结果集为空，则返回错误。
func (tx *Tx) Take (pointer interface{}, query string, args ...interface{}) error {
	return Take(tx, pointer, query, args...)
}

// PrepareX  a statement within a transaction.
func (tx *Tx) PrepareX(query string) (*Stmt, error) {
	return PrepareX(tx, query)
}

// PrepareNamed 返回一个 *sqlx.NamedStmt
func (tx *Tx) PrepareN(query string) (*StmtN, error) {
	return prepareNamed(tx, query)
}

// StmtX 返回在事务中运行的 prepared语句的版本。提供的stmt可以是 *sql.Stmt 或 *sqlx.Stmt。
func (tx *Tx) StmtX(stmt interface{}) *Stmt {
	var s *sql.Stmt
	switch v := stmt.(type) {
	case Stmt:
		s = v.Stmt
	case *Stmt:
		s = v.Stmt
	case *sql.Stmt:
		s = v
	default:
		panic(fmt.Sprintf("non-statement type %v passed to Stmtx", reflect.ValueOf(stmt).Type()))
	}
	return &Stmt{Stmt: tx.Stmt(s), Mapper: tx.Mapper}
}

// NamedStmt 返回在事务中运行的prepared语句的版本,返回一个 *sqlx.NamedStmt
func (tx *Tx) StmtN(stmt *StmtN) *StmtN {
	return &StmtN{
		QueryString: stmt.QueryString,
		Params:      stmt.Params,
		Stmt:        tx.StmtX(stmt.Stmt),
	}
}



